/* 
 * Quechua - the lightweight data mining framework
 *
 * Copyright (C) 2012 Marek Denis <quechua@octogan.net>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <cstdio>
#include <cstdlib>
#include <getopt.h> // for getopt
#include <string>

#include <glibmm/init.h>
#include <glibmm/thread.h>
#include <glibmm/threadpool.h>
#include <glibmm/random.h>
#include <glibmm/timer.h>
#include <ev++.h>
#include <libconfig.h++>

#include "../include/log.h"
#include "../include/quechua.h"

Quechua *g_quechua = NULL;

using namespace std;
using namespace libconfig;

void usage() {
    LOG(INFO) << "Quechua usage: ./quechua [-c <config>] [-l<loglevel>] [-D]\n"
              << "For more info see http://code.google.com/p/quechua\nExiting";
    exit(0);
}

int main(int argc, char* argv[]) {
    
    Glib::init();
    Glib::thread_init(NULL);
    if(!(g_quechua = new(nothrow) Quechua())) {
        LOG(CRITIC) << "Error creating Quechua instance, exiting...";
        exit(EXIT_FAILURE);
    }

    
    int option;
    // read config
    while((option = getopt(argc,argv,"Dc:l:"))!=-1) {
       switch(option) {
           case 'c':
               if(g_quechua && g_quechua->config)
                   g_quechua->config->path = optarg;
               else {
                   LOG(CRITIC) << "Config component is broken or doesn't exist."
                               << "This should never happen, exiting...";
                   exit(EXIT_FAILURE);
               };
               break;
           case 'D':
               {LOG(INFO) << "Daemonizing" ;}
               if(g_quechua && g_quechua->logs) {
                   g_quechua->daemonize();
               } else {
                   LOG(CRITIC) << "Logging component is broken or doesn't exist."
                               << "This should never happen, exiting...";
                   exit(EXIT_FAILURE);
               }
               break;
           case 'l':
               if(g_quechua && g_quechua->logs)
                   g_quechua->logs->set_level(logname2level(optarg));
               else {
                   LOG(CRITIC) << "Logging component is broken or doesn't exist."
                              << " This should never happen, exiting...";
                   exit(EXIT_FAILURE);
               }
               break;
           default:
               usage();
               
       };
    }
    
    int config_result = g_quechua->config->load();
    if(config_result==-1) {
        LOG(ERROR) <<  "No such configuration file, exiting...";
        delete g_quechua;
        exit(EXIT_FAILURE);
    } else if(config_result==-2 || config_result==-3) {
        LOG(ERROR) << "Syntax error in configuration file, exiting...";
        delete g_quechua;
        exit(EXIT_FAILURE);
    }


    if(!g_quechua->config->check_version()) {
        LOG(ERROR) << "Bad config version (should be:\""
                   << g_quechua->config->version
                   << "\"), exiting...";
        delete g_quechua;
        exit(EXIT_FAILURE);
    }

    // handles loading .so modules and indexes them in a std::map
    if(!g_quechua->modules->load_modules(*g_quechua->config->configuration)) {
        LOG(ERROR) << "Error loading modules, exiting...";
        delete g_quechua;
        exit(EXIT_FAILURE);
    }
    
    // bases on the configuration, loads components from previously loaded .so files
    g_quechua->modules->load_components(*g_quechua->config->configuration);

    g_quechua->threads->prepare();
    g_quechua->threads->start();

    /* A perfect place for doing some initial work,
    * like creating sockets, setting up some data structures and so on
    * We start with loggers because they won't start any activity while other components 
    * are not yet ready.
    */

    {LOG(INFO) << "Preparing components...\n";}
    
    g_quechua->prepare_loggers();
    g_quechua->prepare_channels();
    g_quechua->prepare_workflows();

    /* Run all the components in here.
    * In most cases this just launches ev_watchers (calls start() method)
    */

    {LOG(INFO) << "Starting components...\n";}

    g_quechua->start_loggers();
    g_quechua->start_channels();
    g_quechua->start_workflows();
    
    g_quechua->prepare_signals();

    // fires the main ev_loop
    g_quechua->run();

    // stop everything
    
    LOG(INFO) << "Stopping components...\n";


    g_quechua->stop_signals();

    g_quechua->stop_channels();

    g_quechua->threads->stop();
    
    g_quechua->stop_workflows();
    g_quechua->stop_loggers();

    // destroy dynamic objects
    
    {LOG(INFO) << "Destroying components...\n";}
    g_quechua->destroy_channels();
    g_quechua->destroy_loggers();
    g_quechua->destroy_workflows();

    
    delete g_quechua;
    return 0;
}
